package org.afk.sisy.io;

import java.io.*;
import java.nio.file.*;
import java.sql.*;

import org.afk.sisy.*;
import org.afk.sisy.db.*;
import org.afk.tools.log.*;

/**
 * backups, copies and renames the files
 * @author axel
 *
 */
public class CopyJob implements Runnable
{
	private final PathWrapper secondary;
	private final PathWrapper primary;
	private final ErrorHandler errorHandler;
	private final SisyProgress progress;
	private final SisyJob job;
	private final String reason;
	private final PathDataBase pathDataBase;

	public CopyJob(final PathWrapper source, final PathWrapper target, final ErrorHandler errorHandler, final SisyProgress progress, final SisyJob job,
			final String reason, final PathDataBase pathDataBase)
	{
		this.primary = source;
		this.secondary = target;
		this.errorHandler = errorHandler;
		this.progress = progress;
		this.job = job;
		this.reason = reason;
		this.pathDataBase = pathDataBase;
	}

	private Path copy(final PathWrapper source, final Path dest)
	{
		if (source.isDirectory())
			return createDirectory(dest);
		else
			try
			{
				Files.copy(source.getAbsolutPath(), dest, StandardCopyOption.COPY_ATTRIBUTES);
				return dest;
			}
			catch (final IOException e)
			{
				this.errorHandler.warning("While syncing " + source + " to " + dest, e);
				return null;
			}
	}

	private void copyFailed(final PathWrapper source)
	{
		this.errorHandler.warning("Could not copy " + source);
		progress.incError(1);
	}

	private void copyNewPrimary() throws SQLException
	{
		if (copy(this.primary, this.job.getSecondaryDir().resolve(this.primary.getRelativePath())) != null)
			updateDb(this.primary);
		else
			copyFailed(this.primary);
	}

	private void copyNewSecondary() throws SQLException, IOException
	{
		final Path primary = copy(this.secondary, this.job.getPrimaryDir().resolve(this.secondary.getRelativePath()));
		if (primary != null)
		{
			final PathWrapper newPrimary = new PathWrapper(this.job.getPrimaryDir(), primary);
			updateDb(newPrimary);
		}
		else
			copyFailed(this.secondary);

	}

	private String createActivityText()
	{
		if (primary != null && secondary != null)
			return "syncing " + primary.getRelativePath();
		if (primary != null)
			return "new Primary " + primary.getRelativePath();
		if (secondary != null)
			return "new Secondary " + secondary.getRelativePath();
		return "Both are null";
	}

	private Path createDirectory(final Path targetPath)
	{
		try
		{
			return Files.createDirectories(targetPath);
		}
		catch (final IOException e)
		{
			this.errorHandler.warning("Could not create " + targetPath);
			return null;
		}
	}

	private void sync() throws IOException
	{
		if (this.job.getSyncOption() == SyncOption.BIGGER)
		{
			if (this.primary.getSize() >= this.secondary.getSize())
				sync(this.primary, this.secondary);
			else
				sync(this.secondary, this.primary);
		}
		else if (this.job.getSyncOption() == SyncOption.NEWER)
		{
			if (SisyTools.compareLastModified(this.primary.getLastModified(), this.secondary.getLastModified()) <= 0)
				sync(this.primary, this.secondary);
			else
				sync(this.secondary, this.primary);
		}
		else if (this.job.getSyncOption() == SyncOption.PRIMARY)
			sync(this.primary, this.secondary);
		else if (this.job.getSyncOption() == SyncOption.SECONDARY)
			sync(this.secondary, this.primary);
		else if (this.job.getSyncOption() == SyncOption.BOTH)
		{
			// TODO
		}
	}

	private void sync(final PathWrapper source, final PathWrapper dest) throws IOException
	{
		// on demand new Attic, because it should not happen very often
		if (new Attic(job, errorHandler, progress).store(dest))
			copy(source, dest.getAbsolutPath());
		else
			atticFailed(dest);
	}

	private void atticFailed(final PathWrapper dest)
	{
		// TODO Auto-generated method stub

	}

	private void updateDb(final PathWrapper path) throws SQLException
	{
		if (this.pathDataBase != null)
			if (path != null)
				this.pathDataBase.update(path);
	}

	@Override
	public void run()
	{
		final String activity = createActivityText();
		try
		{
			this.progress.getActivities().add(activity);
			this.errorHandler.debug(this.reason + " " + this.primary + " <-> \r\n" + this.secondary);

			if (this.primary == null)
				copyNewSecondary();
			else if (this.secondary == null)
				copyNewPrimary();
			else
				sync();
		}
		catch (final Throwable t)
		{
			this.progress.incError(1);
			this.errorHandler.warning("while processing " + this, t);
		}
		finally
		{
			this.progress.getActivities().remove(activity);
		}
	}

	@Override
	public String toString()
	{
		final StringBuilder builder = new StringBuilder();
		builder.append("CopyJob [");
		if (this.primary != null)
		{
			builder.append("source=");
			builder.append(this.primary);
			builder.append(", ");
		}
		if (this.secondary != null)
		{
			builder.append("target=");
			builder.append(this.secondary);
		}
		builder.append("]");
		return builder.toString();
	}

}
